home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / pascal / ovrsub.com / OVRPREP.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1990-01-05  |  16.6 KB  |  549 lines

  1. (*
  2. OVRPREP extracts overlay information from the EXE file of an overlaid Turbo
  3. Pascal 5.x program and appends it to the OVR file.  This information is used
  4. by the OvrSubstitute procedure in the OVRSUB unit.
  5.  
  6. To use it, first compile the overlaid application to create the EXE and OVR
  7. files and a corresponding MAP file.  OVRPREP must read the first section of the
  8. MAP file (the segment map) to get certain information. It also reads from the
  9. EXE file to get detailed information about the overlays.  This information is
  10. then appended to the OVR file.
  11.  
  12. Call OVRPREP as follows:
  13.  
  14.   OVRPREP [Options] ProgName
  15.  
  16. OVRPREP forces the extensions 'EXE', 'MAP', and 'OVR' onto ProgName to find
  17. the corresponding files.  The only option at present is /Q, which stops OVRPREP
  18. from writing status messages while it works.
  19.  
  20. Format of data appended to OVR file:
  21.  
  22.   for each overlaid unit in program
  23.     StatStart : Word;   {Segment address of static dispatcher}
  24.     FileOfs : LongInt;  {Offset of code segment within overlay file}
  25.     CodeSize : Word;    {Number of bytes of actual code for this unit}
  26.     FixupSize : Word;   {Number of bytes of fixup information for this unit}
  27.     EntryPts : Word;    {Number of entry points (procedures and functions) in unit}
  28.     for each EntryPt
  29.       EntryPointCodeOffset : Word;  {Offset of code entry point within unit}
  30.     end for
  31.   end for
  32.   DataSegStart : Word;  {Segment address of Data segment}
  33.   OverBuf : Word;       {Initial size for overlay buffer in paragraphs}
  34.   DataOfs : LongInt;    {Offset of start of OVRPREP data within OVR file}
  35.   Sig : string[6] = 'OVRSUB';  {Signature string}
  36.  
  37.  
  38. Written by Ron Schuster
  39. Copyright (c) 1989.  All rights reserved.
  40. May be distributed freely, but not for a profit.
  41.  
  42. OVRPREP was derived from OVRSIZ, written by Kim Kokkonen,
  43. TurboPower Software, and used with permission of the author.
  44. Copyright (c) 1989, TurboPower Software. All rights reserved.
  45. May be distributed freely, but not for a profit.
  46.  
  47. Version 1.0, 12/28/89
  48. --------------------
  49.   Initial release.
  50. *)
  51.  
  52. {$R-,S-,I-,V-,F-,B-}
  53.  
  54. program OvrPrep;
  55.   {-Appends overlay data to OVR file for use by OVRSUB unit}
  56.  
  57. uses
  58.   Dos;
  59.  
  60. {$I OVRSUB.INC}
  61.  
  62. const
  63.   Version = '1.0';                {Version number}
  64.   MaxUnits = 255;                 {Maximum units in a program}
  65.   OvrInstr : Word = $3FCD;        {INT 3Fh instruction}
  66.   ShowStatus : Boolean = True;    {True to keep status running during operation}
  67.   BufSize = 1024;                 {Size of text I/O buffer}
  68.   NameSize = 15;                  {Maximum reported segment name length}
  69.   Digits : array[0..$F] of Char = '0123456789ABCDEF';
  70.  
  71. type
  72.   Pathname = String[79];
  73.  
  74.   ExeHeaderRec =                  {Information describing EXE file}
  75.     record
  76.       Signature : Word;           {EXE file signature}
  77.       LengthRem : Word;           {Number of bytes in last page of EXE image}
  78.       LengthPages : Word;         {Number of 512 byte pages in EXE image}
  79.       NumReloc : Word;            {Number of relocation items}
  80.       HeaderSize : Word;          {Number of paragraphs in EXE header}
  81.       MinHeap : Word;             {Minimum extra paragraphs to allow}
  82.       MaxHeap : Word;             {Paragraphs to keep beyond end of image}
  83.       StackSeg : Word;            {Initial stack seg relative to image base}
  84.       StackPtr : Word;            {Initial SP}
  85.       CheckSum : Word;            {EXE file check sum, not used}
  86.       IpInit : Word;              {Initial IP}
  87.       CodeSeg : Word;             {Initial code seg relative to image base}
  88.       RelocOfs : Word;            {Bytes into EXE for first relocation item}
  89.       OverlayNum : Word;          {Overlay number, not used here}
  90.     end;
  91.  
  92.   UnitRecord =                    {Description of each unit in program}
  93.     record
  94.       SegClass : Word;            {0 for code, 1 for data, 2 for stack, 3 for heap}
  95.       StatStart : LongInt;        {Start position within EXE image in bytes}
  96.       StatLen : LongInt;          {Length of unit image in bytes}
  97.       Name : String[NameSize];    {Name of the unit}
  98.     end;
  99.   UnitArray = array[1..MaxUnits] of UnitRecord;
  100.  
  101.  
  102. var
  103.   Units : UnitArray;              {Describes all segments}
  104.   UnitCount : Word;               {Number of units}
  105.   Iname : Pathname;               {Input EXE file name}
  106.   Mname : Pathname;               {Input MAP file name}
  107.   Oname : Pathname;               {Output OVR file name}
  108.   EXEHeader : ExeHeaderRec;       {Header for input EXE file}
  109.   InF : file;                     {Input EXE file}
  110.   OutF : file;                    {Output OVR file}
  111.   StdErr : Text;                  {Standard error device}
  112.   Buffer : array[1..BufSize] of Char; {Text buffer for map file}
  113.   Ovr : StaticDispatcher;         {Overlay info}
  114.  
  115.   procedure WriteCopyRight;
  116.     {-Copyright notice in object code and on screen}
  117.   begin                        
  118.     WriteLn(StdErr, 'OVRPREP, by Kim Kokkonen and Ron Schuster.  Version ', Version);
  119.   end;                      
  120.  
  121.   procedure OpenStdErr;
  122.     {-Open standard error device}
  123.   begin
  124.     Assign(StdErr, '');
  125.     Rewrite(StdErr);
  126.     with TextRec(StdErr) do begin
  127.       Handle := 2;
  128.       BufSize := 1;
  129.     end;
  130.   end;
  131.  
  132.   procedure Error(Msg : String);
  133.     {-Report error and halt}
  134.   begin
  135.     if Msg <> '' then
  136.       WriteLn(Msg);
  137.     Halt(1);
  138.   end;
  139.  
  140.   procedure InvalidMapError;
  141.     {-Common error}
  142.   begin
  143.     Error('Invalid MAP file format');
  144.   end;
  145.  
  146.   procedure ErrorExeRead;
  147.     {-Common error}
  148.   begin
  149.     Error('Error reading EXE file');
  150.   end;
  151.  
  152.   function Long2Str(L : LongInt) : String;
  153.     {-Convert a long/word/integer/byte/shortint to a string}
  154.   var
  155.     S : String;
  156.   begin
  157.     Str(L, S);
  158.     Long2Str := S;
  159.   end;
  160.  
  161.   function StUpcase(S : String) : String;
  162.     {-Return the uppercase of a string}
  163.   var
  164.     I : Integer;
  165.   begin
  166.     for I := 1 to Length(S) do
  167.       S[I] := Upcase(S[I]);
  168.     StUpcase := S;
  169.   end;
  170.  
  171.   function TrimLead(S : String) : String;
  172.     {-Return a string with leading white space removed}
  173.   begin
  174.     while (Length(S) > 0) and (S[1] <= ' ') do
  175.       Delete(S, 1, 1);
  176.     TrimLead := S;
  177.   end;
  178.  
  179.   function Trim(S : String) : String;
  180.     {-Return a string with leading and trailing white space removed}
  181.   begin
  182.     while (Length(S) > 0) and (S[Length(S)] <= ' ') do
  183.       Dec(S[0]);
  184.     while (Length(S) > 0) and (S[1] <= ' ') do
  185.       Delete(S, 1, 1);
  186.     Trim := S;
  187.   end;
  188.  
  189.   function HasExtension(Name : String; var DotPos : Word) : Boolean;
  190.     {-Return whether and position of extension separator dot in a pathname}
  191.   var
  192.     I : Word;
  193.   begin
  194.     DotPos := 0;
  195.     for I := Length(Name) downto 1 do
  196.       if (Name[I] = '.') and (DotPos = 0) then
  197.         DotPos := I;
  198.     HasExtension := (DotPos > 0) and (Pos('\', Copy(Name, Succ(DotPos), 64)) = 0);
  199.   end;
  200.  
  201.   function ForceExtension(Name, Ext : String) : String;
  202.     {-Return a pathname with the specified extension attached}
  203.   var
  204.     DotPos : Word;
  205.   begin
  206.     if HasExtension(Name, DotPos) then
  207.       ForceExtension := Copy(Name, 1, DotPos)+Ext
  208.     else
  209.       ForceExtension := Name+'.'+Ext;
  210.   end;
  211.  
  212.   function BlkRead(var F : file; var Buffer; Size : Word) : Boolean;
  213.     {-Convenient shell around BlockRead}
  214.   var
  215.     BytesRead : Word;
  216.   begin
  217.     BlockRead(F, Buffer, Size, BytesRead);
  218.     BlkRead := (IoResult = 0) and (BytesRead = Size);
  219.   end;
  220.  
  221.   function FileNewer(FileA, FileB : String) : Boolean;
  222.     {-Return true if FileA is newer than FileB, both known to exist}
  223.   var
  224.     FA : file;
  225.     FB : file;
  226.     TA : LongInt;
  227.     TB : LongInt;
  228.   begin
  229.     Assign(FA, FileA);
  230.     Reset(FA);
  231.     Assign(FB, FileB);
  232.     Reset(FB);
  233.     GetFtime(FA, TA);
  234.     GetFtime(FB, TB);
  235.     Close(FA);
  236.     Close(FB);
  237.     FileNewer := (TA > TB);
  238.   end;
  239.  
  240.   procedure WriteHelp;
  241.     {-Display help information and halt}
  242.   begin
  243.     WriteLn;
  244.     WriteLn('Usage: OVRPREP [Options] InputName');
  245.     WriteLn;
  246.     WriteLn('  OVRPREP must access:');
  247.     WriteLn('    InputName.EXE - overlaid executable file.');
  248.     WriteLn('    InputName.MAP - symbol file for segment information.');
  249.     WriteLn('    InputName.OVR - overlay file for appending data.');
  250.     WriteLn;
  251.     WriteLn('Options:');
  252.     WriteLn('  /Q    Quiet mode. No status output while processing.');
  253.     Halt(1);
  254.   end;
  255.  
  256.   function ExistFile(FName : String) : Boolean;
  257.     {-Return true if file exists}
  258.   var
  259.     F : file;
  260.   begin
  261.     Assign(F, FName);
  262.     Reset(F);
  263.     if IoResult = 0 then begin
  264.       ExistFile := True;
  265.       Close(F);
  266.     end else
  267.       ExistFile := False;
  268.   end;
  269.  
  270.   procedure ValidateInput;
  271.     {-Get working filenames and assure files exist}
  272.   var
  273.     Iroot : Pathname;
  274.     Arg : String;
  275.     I : Integer;
  276.   begin
  277.     {Get parameters}
  278.     Iroot := '';
  279.     I := 1;
  280.     while I <= ParamCount do begin
  281.       Arg := StUpcase(ParamStr(I));
  282.       if (Arg = '/Q') or (Arg = '-Q') then
  283.         ShowStatus := False
  284.       else if Iroot = '' then
  285.         Iroot := Arg
  286.       else
  287.         Error('Too many filenames on command line');
  288.       Inc(I);
  289.     end;
  290.     if (Iroot = '') then
  291.       WriteHelp;
  292.  
  293.     {Build working filenames}
  294.     Iname := ForceExtension(Iroot, 'EXE');
  295.     Mname := ForceExtension(Iroot, 'MAP');
  296.     Oname := ForceExtension(Iroot, 'OVR');
  297.     {Make sure files are OK}
  298.     if not ExistFile(Iname) then
  299.       Error('EXE file '+Iname+' not found');
  300.     if not ExistFile(Mname) then
  301.       Error('MAP file '+Mname+' not found');
  302.     if not ExistFile(Oname) then
  303.       Error('OVR file '+Oname+' not found');
  304.     if FileNewer(Iname, Mname) then
  305.       Error('MAP file is older than EXE file');
  306.   end;
  307.  
  308.   function GetLong(var S : String; var L : LongInt) : Boolean;
  309.     {-Parse next longint out of line S}
  310.   var
  311.     Num : String[8];
  312.     Code : Word;
  313.   begin
  314.     S := TrimLead(S);
  315.     Num := '';
  316.     while (Length(S) > 0) and (Pos(S[1], Digits) <> 0) do begin
  317.       Num := Num+S[1];
  318.       Delete(S, 1, 1);
  319.     end;
  320.     if Length(Num) = 0 then begin
  321.       GetLong := False;
  322.       Exit;
  323.     end;
  324.     if (Length(S) > 0) and (Upcase(S[1]) = 'H') then begin
  325.       Num := '$'+Num;
  326.       Delete(S, 1, 1);
  327.     end;
  328.     Val(Num, L, Code);
  329.     GetLong := (Code = 0);
  330.   end;
  331.  
  332.   function GetName(var S, Name : String) : Boolean;
  333.     {-Parse next alphanumeric name from string s}
  334.   begin
  335.     S := TrimLead(S);
  336.     Name := '';
  337.     while (Length(S) > 0) and (S[1] > ' ') do begin
  338.       if Length(Name) < NameSize then
  339.         Name := Name+S[1];
  340.       Delete(S, 1, 1);
  341.     end;
  342.     GetName := (Name <> '');
  343.   end;
  344.  
  345.   function NextPara(Bytes : LongInt) : LongInt;
  346.     {-Round up to next paragraph}
  347.   begin
  348.     NextPara := (Bytes+15) and $FFFFFFF0;
  349.   end;
  350.  
  351.   procedure ParseMapFile(FName : String);
  352.     {-Read and parse the MAP file, guaranteed to exist}
  353.   var
  354.     F : Text;
  355.     S : String;
  356.     SegType : String;
  357.     Tlong : LongInt;
  358.     ParseState : (Unknown, Segments, Done);
  359.   begin
  360.  
  361.     {Open up the MAP file for reading}
  362.     Assign(F, FName);
  363.     SetTextBuf(F, Buffer, BufSize);
  364.     Reset(F);
  365.     if IoResult <> 0 then
  366.       Error('Error opening '+FName);
  367.  
  368.     if ShowStatus then
  369.       WriteLn(StdErr, 'Parsing MAP file');
  370.  
  371.     {Parse the segment description section only}
  372.     UnitCount := 0;
  373.     ParseState := Unknown;
  374.     repeat
  375.       ReadLn(F, S);
  376.       if IoResult <> 0 then
  377.         Error('Error reading '+FName);
  378.       S := StUpcase(Trim(S));
  379.       if S <> '' then
  380.         if Pos('START', S) = 1 then
  381.           ParseState := Segments
  382.         else if Pos('ADDRESS', S) = 1 then
  383.           ParseState := Done
  384.         else if ParseState = Segments then begin
  385.           {Parse the line to get the unit description}
  386.           Inc(UnitCount);
  387.           if UnitCount > MaxUnits then
  388.             Error('Cannot exceed '+Long2Str(MaxUnits)+' segments');
  389.           FillChar(Units[UnitCount], SizeOf(UnitRecord), 0);
  390.  
  391.           with Units[UnitCount] do begin
  392.  
  393.             {Get the position and size of the unit in the EXE image}
  394.             if not GetLong(S, StatStart) then
  395.               InvalidMapError;
  396.             {Ignore the end of the segment}
  397.             if not GetLong(S, Tlong) then
  398.               InvalidMapError;
  399.             {Get the length of the segment}
  400.             if not GetLong(S, StatLen) then
  401.               InvalidMapError;
  402.  
  403.             {Get the name of the segment}
  404.             if not GetName(S, Name) then
  405.               InvalidMapError;
  406.  
  407.             {Some segments are not really in the EXE file}
  408.             if not GetName(S, SegType) then
  409.               InvalidMapError;
  410.             if SegType = 'CODE' then
  411.               SegClass := 0
  412.             else if SegType = 'DATA' then
  413.               SegClass := 1
  414.             else if SegType = 'STACK' then
  415.               SegClass := 2
  416.             else if SegType = 'HEAP' then
  417.               SegClass := 3
  418.             else
  419.               SegClass := 4;
  420.           end;
  421.         end;
  422.     until (ParseState = Done) or EoF(F);
  423.     Close(F);
  424.   end;
  425.  
  426.   procedure GetEXEInfo(FName, OName : String);
  427.     {-Open EXE and OVR files, read overlay information from EXE, append to OVR}
  428.   var
  429.     V : Integer;
  430.     I : Integer;
  431.     OverBuf : LongInt;
  432.     OverSiz : LongInt;
  433.     TWord : Word;
  434.   begin
  435.     OverBuf := 0;
  436.  
  437.     Assign(InF, FName);
  438.     Reset(InF, 1);
  439.     if IoResult <> 0 then
  440.       Error('Error opening EXE file '+FName);
  441.  
  442.     Assign(OutF, OName);
  443.     Reset(OutF, 1);
  444.     if IoResult <> 0 then
  445.       Error('Error opening OVR file '+OName);
  446.  
  447.     {Check if OVR file already has data appended}
  448.     Seek(OutF, FileSize(OutF)-SizeOf(Trailer));
  449.     if IoResult <> 0 then
  450.       Error('Error seeking OVR file '+OName);
  451.     if not BlkRead(OutF, Trailer, SizeOf(Trailer)) then
  452.       Error('Error reading OVR file');
  453.  
  454.     {If no data appended, get file size}
  455.     if Trailer.Sig <> OvrSubSignature then
  456.       Trailer.OldFileSize := FileSize(OutF);
  457.  
  458.     {Seek to end of file (or former end of file if data is appended)}
  459.     Seek (OutF, Trailer.OldFileSize);
  460.     if IoResult <> 0 then
  461.       Error('Error seeking OVR file '+OName);
  462.  
  463.     if ShowStatus then
  464.       WriteLn(StdErr, 'Reading EXE header information');
  465.  
  466.     {Read the existing EXE header}
  467.     if not BlkRead(InF, EXEHeader, SizeOf(ExeHeaderRec)) then
  468.       ErrorExeRead;
  469.  
  470.     with EXEHeader do begin
  471.       {Assure it's a valid EXE file}
  472.       if Signature <> $5A4D then
  473.         Error('Invalid EXE format for '+FName);
  474.  
  475.       if ShowStatus then
  476.         WriteLn(StdErr, 'Collecting overlay information');
  477.  
  478.       for V := UnitCount downto 1 do
  479.         with Units[V], Ovr do
  480.           if (SegClass = 0) and (StatLen > 0) then begin
  481.             {Get to right spot in old EXE file}
  482.             Seek(InF, (LongInt(HeaderSize) shl 4)+StatStart);
  483.  
  484.             {Read the start of unit into the overlay record}
  485.             if not BlkRead(InF, Ovr, Ofs(Vectors)-Ofs(Ovr)) then
  486.               ErrorExeRead;
  487.  
  488.             {If it's an overlaid unit, get entry point info and append to OVR}
  489.             if ReturnInt = OvrInstr then begin
  490.               if not BlkRead(InF, Vectors, SizeOf(VectorRec)*EntryPts) then
  491.                 ErrorExeRead;
  492.               TWord := StatStart shr 4;
  493.               BlockWrite(OutF, TWord, SizeOf(TWord));
  494.               BlockWrite(OutF, FileOfs, SizeOf(FileOfs));
  495.               BlockWrite(OutF, CodeSize, SizeOf(CodeSize));
  496.               BlockWrite(OutF, FixupSize, SizeOf(FixupSize));
  497.               BlockWrite(OutF, EntryPts, SizeOf(EntryPts));
  498.  
  499.               for I := 1 to EntryPts do
  500.                 BlockWrite (OutF, Vectors[I].CodeOffset, SizeOf(Word));
  501.  
  502.               OverSiz := NextPara(CodeSize)+NextPara(FixupSize);
  503.               if OverSiz > OverBuf then
  504.                 OverBuf := OverSiz;
  505.             end;
  506.           end
  507.     end;
  508.  
  509.     {Write data about Data segment}
  510.     V := UnitCount;
  511.     while V > 0 do
  512.       with Units[V], Ovr do
  513.         if Name = 'DATA' then begin
  514.           TWord := StatStart shr 4;
  515.           BlockWrite(OutF, TWord, SizeOf(TWord));
  516.           V := 0;
  517.         end
  518.         else
  519.           Dec(V);
  520.  
  521.     {Write new value for initial overlay buffer size}
  522.     TWord := OverBuf shr 4;
  523.     BlockWrite(OutF, TWord, SizeOf(TWord));
  524.  
  525.     {Write trailer}
  526.     Trailer.Sig := OvrSubSignature;
  527.     BlockWrite(OutF, Trailer, SizeOf(Trailer));
  528.  
  529.     Close(InF);
  530.     Close(OutF);
  531.   end;
  532.  
  533. begin
  534.   {Open standard error device}
  535.   OpenStdErr;
  536.  
  537.   {Display copyright}
  538.   WriteCopyRight;
  539.  
  540.   {Get filenames and assure they exist}
  541.   ValidateInput;
  542.  
  543.   {Parse MAP file to get segment names and locations}
  544.   ParseMapFile(Mname);
  545.  
  546.   {Read overlay information from EXE file and append to OVR file}
  547.   GetEXEInfo(Iname, Oname);
  548. end.
  549.